package io.lkt.common.redis;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

@Component
public class RedisCacheUtil<T>
{
    private static final Logger log = LoggerFactory.getLogger(RedisCacheUtil.class);
    @Autowired
    @Qualifier("redisTemplate")
    public RedisTemplate redisTemplate;
    @Autowired
    @Qualifier("redisTemplateMaster")
    public RedisTemplate redisTemplateMaster;

    @Autowired(required=false)
    public void setRedisTemplate(RedisTemplate redisTemplate)
    {
        RedisSerializer stringSerializer = new StringRedisSerializer();
        RedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        redisTemplate.setDefaultSerializer(stringSerializer);
        redisTemplate.setEnableDefaultSerializer(true);

        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);
        redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();

        this.redisTemplate = redisTemplate;
    }

    public <T> ValueOperations<String, T> setCacheObject(String key, T value, long time)
    {
        log.info("[setObject-key] = " + key + " [value] = " + value);

        ValueOperations<String, T> operation = this.redisTemplate.opsForValue();

        operation.set(key, value, time, TimeUnit.SECONDS);

        return operation;
    }

    public <T> ValueOperations<String, T> setCacheObjectMaster(String key, T value, long time)
    {
        log.info("[setObject-key] = " + key + " [value] = " + value);

        ValueOperations<String, T> operation = this.redisTemplateMaster.opsForValue();

        operation.set(key, value, time, TimeUnit.SECONDS);

        return operation;
    }

    public <T> T getCacheObject(String key)
    {
//        log.info("[getObject-key] = " + key);

        ValueOperations<String, T> operation = this.redisTemplate.opsForValue();

        T value = operation.get(key);

//        log.info("[getObject-key] = " + key + " [getObject-value] = " + value);
        return value;
    }

    public <T> T getCacheObjectMaster(String key)
    {
//        log.info("[getObject-key] = " + key);

        ValueOperations<String, T> operation = this.redisTemplateMaster.opsForValue();

        T value = operation.get(key);

//        log.info("[getObject-key] = " + key + " [getObject-value] = " + value);
        return value;
    }

    public <T> ListOperations<String, T> setCacheList(String key, List<T> dataList)
    {
        ListOperations listOperation = this.redisTemplate.opsForList();
        if (null != dataList)
        {
            int size = dataList.size();
            for (int i = 0; i < size; i++) {
                listOperation.rightPush(key, dataList.get(i));
            }
        }
        return listOperation;
    }

    public <T> List<T> getCacheList(String key)
    {
        List<T> dataList = new ArrayList();

        ListOperations<String, T> listOperation = this.redisTemplate.opsForList();

        Long size = listOperation.size(key);
        for (int i = 0; i < size.longValue(); i++) {
            dataList.add(listOperation.leftPop(key));
        }
        return dataList;
    }

    public <T> BoundSetOperations<String, T> setCacheSet(String key, Set<T> dataSet)
    {
        BoundSetOperations<String, T> setOperation = this.redisTemplate.boundSetOps(key);

        Iterator<T> it = dataSet.iterator();
        while (it.hasNext()) {
            setOperation.add((T) new Object[] { it.next() });
        }
        return setOperation;
    }

    public Set<T> getCacheSet(String key)
    {
        Set<T> dataSet = new HashSet();

        BoundSetOperations<String, T> operation = this.redisTemplate.boundSetOps(key);

        Long size = operation.size();
        for (int i = 0; i < size.longValue(); i++) {
            dataSet.add(operation.pop());
        }
        return dataSet;
    }

    public <T> HashOperations<String, String, T> setCacheMap(String key, Map<String, T> dataMap)
    {
        HashOperations hashOperations = this.redisTemplate.opsForHash();
        if (null != dataMap) {
            for (Map.Entry<String, T> entry : dataMap.entrySet()) {
                hashOperations.put(key, entry.getKey(), entry.getValue());
            }
        }
        return hashOperations;
    }

    public <T> Map<String, T> getCacheMap(String key)
    {
        Map<String, T> map = this.redisTemplate.opsForHash().entries(key);

        return map;
    }

    public <T> HashOperations<String, Integer, T> setCacheIntegerMap(String key, Map<Integer, T> dataMap)
    {
        HashOperations hashOperations = this.redisTemplate.opsForHash();
        if (null != dataMap) {
            for (Map.Entry<Integer, T> entry : dataMap.entrySet()) {
                hashOperations.put(key, entry.getKey(), entry.getValue());
            }
        }
        return hashOperations;
    }

    public <T> Map<Integer, T> getCacheIntegerMap(String key)
    {
        Map<Integer, T> map = this.redisTemplate.opsForHash().entries(key);

        return map;
    }

    public boolean removeSingleCache(String key)
    {
        try
        {
            this.redisTemplate.delete(key);
            return true;
        }
        catch (Throwable t)
        {
            log.error("获取缓存失败key[" + key + ", error[" + t + "]");
        }
        return false;
    }

    public boolean removeSingleCacheMaster(String key)
    {
        try
        {
            this.redisTemplateMaster.delete(key);
            return true;
        }
        catch (Throwable t)
        {
            log.error("获取缓存失败key[" + key + ", error[" + t + "]");
        }
        return false;
    }

    public Set<T> getCacheSetForRandomEle(String key, long count)
    {
        log.info("[getCacheSetForRandomEle] = " + key);
        Set<T> dataSet = this.redisTemplate.opsForSet().distinctRandomMembers(key, count);

        return dataSet;
    }

    public Long addEleToSet(String key, T value)
    {
        return this.redisTemplate.opsForSet().add(key, new Object[] { value });
    }

    public boolean removeEleInSet(String key, Object o)
    {
        Long result = this.redisTemplate.opsForSet().remove(key, new Object[] { o });
        checkResult(result);
        return result.longValue() == 1L;
    }

    protected void checkResult(@Nullable Object obj)
    {
        if (obj == null) {
            throw new IllegalStateException("Cannot read collection with Redis connection in pipeline/multi-exec mode");
        }
    }
}
